// Perry Kivolowitz
// Positively Ancient Code!

#include	<math.h>
#include	<assert.h>
#include	<gl/freeglut.h>

bool wire_frame = true;

char *	AppName	= "Articulated Arm Exercise";
GLfloat	ShoulderThetaX , ShoulderThetaZ;
GLfloat	ElbowThetaX , ElbowThetaZ;
GLfloat	HandThetaX , HandThetaY;

void	Joint(void)
{
	//	Joint creates a sphere of radius 0.5 located at the current frame's origin.

	GLfloat	ambient[4]	= { 0.09f , 0.02f , 0.05f , 0.07f };
	GLfloat	diffuse[4]	= { 0.88f , 0.23f , 0.54f , 0.75f };
	GLfloat	specular[4]	= { 1.00f , 1.00f , 1.00f , 1.00f };

	glMaterialf( GL_FRONT , GL_SHININESS , 100.0f);
	glMaterialfv(GL_FRONT , GL_AMBIENT   , ambient);
	glMaterialfv(GL_FRONT , GL_DIFFUSE   , diffuse);
	glMaterialfv(GL_FRONT , GL_SPECULAR  , specular);
	glPushMatrix();
	glScalef(1.0f, .7f, 1.0f);
	glutSolidSphere(0.5 , 30 , 30);
	glPopMatrix();
	
}

void	Hand(void)
{
	GLUquadricObj *	q	= gluNewQuadric();

	GLfloat	ambient[4] = { 0.09f, 0.09f, 0.05f, 1.0f };
	GLfloat	diffuse[4] = { 0.81f, 0.88f, 0.23f, 1.0f };
	GLfloat	specular[4] = { 1.00f, 1.00f, 1.00f, 1.00f };

	glMaterialf( GL_FRONT , GL_SHININESS , 100.0f);
	glMaterialfv(GL_FRONT , GL_AMBIENT   , ambient);
	glMaterialfv(GL_FRONT , GL_DIFFUSE   , diffuse);
	glMaterialfv(GL_FRONT , GL_SPECULAR  , specular);

	assert(q);
	if (q)
	{
		glPushMatrix();
		glTranslatef(0.0f, 0.0f, 0.05f);
		gluDisk(q , 0.0, 0.5, 30, 20);
		glPopMatrix();

		glPushMatrix();
		glTranslatef(0.0f, 0.0f, -0.05f);
		gluCylinder(q, 0.5, 0.5, 0.1, 30, 10);
		glRotatef(180.0f, 0.0f, 1.0f, 0.0f);
		gluDisk(q, 0.0, 0.5, 30, 20);
		glPopMatrix();

		gluDeleteQuadric(q);
	}
}

void	ArmSegment(void)
{
	GLUquadricObj *	q = gluNewQuadric();
	GLfloat	ambient[4] = { 0.05f, 0.02f, 0.09f, 0.07f };
	GLfloat	diffuse[4] = { 0.54f, 0.23f, 0.88f, 0.75f };
	GLfloat	specular[4] = { 1.00f, 1.00f, 1.00f, 1.00f };

	glMaterialf(GL_FRONT, GL_SHININESS, 100.0f);
	glMaterialfv(GL_FRONT, GL_AMBIENT, ambient);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse);
	glMaterialfv(GL_FRONT, GL_SPECULAR, specular);

	assert(q);
	if (q)
	{
		glPushMatrix();
		glRotatef(90.0f , 1.0f , 0.0f , 0.0f);

		glutSolidCube(2);
		//gluCylinder(q , 0.503 , 0.503 , 2.0 , 30 , 30);
		glPopMatrix();
		gluDeleteQuadric(q);
	}
}

void	ReshapeFunc(int	w , int h)
{
	if (h <= 0) return;

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(40.0 , ((double) w) / ((double) h) , 2.0 , 20.0);
	glViewport(0 , 0 , w , h);
}

void	ArticulatedArm(GLfloat SThetaX , GLfloat SThetaZ , GLfloat EThetaX , GLfloat EThetaZ , GLfloat HThetaX , GLfloat HThetaY)
{
	glPushMatrix();

	// Draw Shoulder Joint

	Joint();

	// Implement shoulder rotations

	glRotatef(SThetaX , 1.0 , 0.0 , 0.0);
	glRotatef(SThetaZ , 0.0 , 0.0 , 1.0);

	ArmSegment();

	// Draw Elbow Joint

	glTranslatef(0.0 , -2.0 , 0.0);
	Joint();

	// Implement elbow rotations

	glRotatef(EThetaX , 1.0 , 0.0 , 0.0);
	glRotatef(EThetaZ , 0.0 , 0.0 , 1.0);

	ArmSegment();

	// Draw wrist Joint

	glTranslatef(0.0 , -2.0 , 0.0);
	Joint();

	// Implement wrist rotations

	glTranslatef(0.0 , -1.0 , 0.0);
	glTranslatef(0.0 , 0.5 , 0.0);
	glRotatef(HThetaX , 1.0 , 0.0 , 0.0);
	glTranslatef(0.0 , -0.5 , 0.0);
	glRotatef(HThetaY , 0.0 , 1.0 , 0.0);
	Hand();

	glPopMatrix();
}

void KeyboardFunc(unsigned char c, int x, int y)
{
	switch (c)
	{
	case 27:
	case 'x':
		glutLeaveMainLoop();
		return;

	case 'w':
		wire_frame = !wire_frame;
		break;
	}
	glutPostRedisplay();
}

void	Display(void)
{
	static	GLfloat	light_position[] = { 0.0 , 0.0 , 10.0 , 1.0 };

	glPolygonMode(GL_FRONT_AND_BACK, wire_frame ? GL_LINE : GL_FILL);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(0.0 , -2.0 , 10.0 , 0.0 , -2.0 , 0.0 , 0.0 , 1.0 , 0.0);
	glLightfv(GL_LIGHT0 , GL_POSITION , light_position);

	ArticulatedArm(ShoulderThetaX , ShoulderThetaZ , ElbowThetaX , ElbowThetaZ , HandThetaX , HandThetaY);

	glutSwapBuffers();

}

void	SpecialFunc(int key , int x , int y)
{
	switch (key)
	{

	case GLUT_KEY_F1:

		ShoulderThetaX += 1.0;
		break;

	case GLUT_KEY_F2:

		ShoulderThetaX -= 1.0;
		break;

	case GLUT_KEY_F3:

		ShoulderThetaZ += 1.0;
		break;

	case GLUT_KEY_F4:

		ShoulderThetaZ -= 1.0;
		break;

	case GLUT_KEY_F5:

		ElbowThetaX += 1.0;
		break;

	case GLUT_KEY_F6:

		ElbowThetaX -= 1.0;
		break;

	case GLUT_KEY_F7:

		ElbowThetaZ += 1.0;
		break;

	case GLUT_KEY_F8:

		ElbowThetaZ -= 1.0;
		break;

	case GLUT_KEY_F9:

		HandThetaX += 1.0;
		break;

	case GLUT_KEY_F10:

		HandThetaX -= 1.0;
		break;

	case GLUT_KEY_F11:

		if (glutGetModifiers() == GLUT_ACTIVE_SHIFT)
		{
			HandThetaY -= 1.0;
		}
		else
		{
			HandThetaY += 1.0;
		}
		break;
	}
	Display();
}

int	main(int argc , char * argv[])
{
	HandThetaX = HandThetaY = ShoulderThetaX = ShoulderThetaZ = ElbowThetaX = ElbowThetaZ = 0.0;

	glutInit(&argc , argv);
	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
	glutInitWindowSize(512 , 512);
	glutInitWindowPosition(0 , 0);
	glutCreateWindow(AppName);
	glutDisplayFunc(Display);
	glutSpecialFunc(SpecialFunc);
	glutKeyboardFunc(KeyboardFunc);
	glutReshapeFunc(ReshapeFunc);

	glClearColor(0.0 , 0.0 , 0.0 , 0.0);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glEnable(GL_NORMALIZE);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_CULL_FACE);
	glLightModelf(GL_LIGHT_MODEL_TWO_SIDE , 0.0);
	glLightModelf(GL_LIGHT_MODEL_LOCAL_VIEWER , 1.0);
	glutMainLoop();

	return 0;
}